package com.chenfan.mcn.service.impl;

import cn.hutool.core.util.NumberUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import com.alibaba.excel.write.style.column.AbstractColumnWidthStyleStrategy;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.chenfan.common.exception.BusinessException;
import com.chenfan.mcn.clients.baseinfo.BaseDictsVO;
import com.chenfan.mcn.clients.baseinfo.BaseInfoClient;
import com.chenfan.mcn.clients.baseinfo.BaseSecondlevelDictsVO;
import com.chenfan.mcn.clients.baseinfo.DescByCodeDTO;
import com.chenfan.mcn.component.FilestoreHelper;
import com.chenfan.mcn.config.UserVoContextHolder;
import com.chenfan.mcn.constant.*;
import com.chenfan.mcn.dao.*;
import com.chenfan.mcn.enums.*;
import com.chenfan.mcn.exception.McnErrorCode;
import com.chenfan.mcn.extension.excel.DynamicExcel;
import com.chenfan.mcn.component.CodeGenerateHelper;
import com.chenfan.common.extension.lock.DistributedLockTemplate;
import com.chenfan.mcn.model.*;
import com.chenfan.mcn.model.dto.*;
import com.chenfan.mcn.model.vo.*;
import com.chenfan.mcn.service.ApprovalCallbackService;
import com.chenfan.mcn.service.ApprovalFlowService;
import com.chenfan.mcn.service.StarRateCardConfigService;
import com.chenfan.mcn.service.StarRateCardService;
import com.chenfan.mcn.utils.BusinessAssert;
import com.chenfan.mcn.utils.CommonUtil;
import com.chenfan.mcn.utils.DateUtil;
import com.chenfan.mcn.utils.NoRepeatableArrayList;
import com.chenfan.notify.MessageClient;
import com.chenfan.notify.vo.MessageDetailVo;
import com.chenfan.notify.vo.MessagePlanVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.compress.utils.Lists;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.poi.ss.usermodel.*;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.ByteArrayOutputStream;
import java.math.BigDecimal;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 红人刊例管理服务实现类
 *
 * @author wulg
 * @date 2021-12-30
 **/
@Slf4j
@Service
public class StarRateCardServiceImpl implements StarRateCardService, ApprovalCallbackService {

    @Autowired
    private StarRateCardMapper starRateCardMapper;

    @Autowired
    private StarRateCardDetailMapper starRateCardDetailMapper;

    @Autowired
    private StarRateCardConfigMapper starRateCardConfigMapper;

    @Autowired
    private StarRateCardConfigService starRateCardConfigService;

    @Autowired
    private StarPlatformInfoMapper starPlatformInfoMapper;

    @Autowired
    private StarQuoteWayMapper starQuoteWayMapper;

    @Autowired
    private ApprovalFlowService approvalFlowService;

    @Autowired
    private StarBasicDataMapper starBasicDataMapper;

    @Autowired
    private MessageClient messageClient;

    @Autowired
    private BaseInfoClient baseInfoClient;

    @Value("${web_image_prefix}")
    private String webImagePrefix;

    @SuppressWarnings("AliDeprecation")
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Long saveStarRateCard(StarRateCardSaveDTO dto) {
        BusinessAssert.isTrue(starRateCardMapper.notExist(Wrappers.lambdaQuery(StarRateCard.class)
                .eq(StarRateCard::getCardName, dto.getCardName())
                .ne(StarRateCard::getStatus, ApprovalStatusEnum.INVALID.getCode())
                .eq(StarRateCard::getIsDelete, NumberCst.ZERO)
                .ne(Objects.nonNull(dto.getId()), StarRateCard::getId, dto.getId())), McnErrorCode.DATA_EXISTED, "该刊例名称已存在");
        BusinessAssert.isTrue(starRateCardMapper.notExist(Wrappers.lambdaQuery(StarRateCard.class)
                .eq(StarRateCard::getIsDelete, NumberCst.ZERO)
                .and(wrapper -> wrapper.or(a -> a.ge(StarRateCard::getEffectiveStartDate, dto.getEffectiveStartDate()).le(StarRateCard::getEffectiveStartDate, dto.getEffectiveEndDate()))
                                       .or(b -> b.ge(StarRateCard::getEffectiveEndDate, dto.getEffectiveStartDate()).le(StarRateCard::getEffectiveEndDate, dto.getEffectiveEndDate()))
                                       .or(c -> c.le(StarRateCard::getEffectiveStartDate, dto.getEffectiveStartDate()).ge(StarRateCard::getEffectiveEndDate, dto.getEffectiveEndDate()))
                ).ne(StarRateCard::getStatus, ApprovalStatusEnum.INVALID.getCode())
                .ne(Objects.nonNull(dto.getId()), StarRateCard::getId, dto.getId())), "已存在相同生效时段的非作废状态的刊例，请修改！");
        if(Objects.isNull(dto.getId())) {
            StarRateCard starRateCard = new StarRateCard();
            starRateCard.setStatus(ApprovalStatusEnum.UN_COMMIT.getCode());
            starRateCard.setCardCode(CodeGenerateHelper.getCode(BillCodeCst.STAR_RATE_CARD));
            List<StarRateCardConfig> configList = starRateCardConfigService.getNewestConfig();
            BusinessAssert.notEmpty(configList, McnErrorCode.DATA_NOT_FOUND, "查询不到有用的刊例配置");
            List<Long> configIdList = configList.stream().map(StarRateCardConfig::getId).collect(Collectors.toList());
            starRateCard.setConfigIds(JSONObject.toJSONString(configIdList));
            starRateCard.setCardName(dto.getCardName());
            starRateCard.setEffectiveStartDate(dto.getEffectiveStartDate());
            starRateCard.setEffectiveEndDate(dto.getEffectiveEndDate());
            starRateCard.setRemark(dto.getRemark());
            starRateCardMapper.insert(starRateCard);
            return starRateCard.getId();
        }
        StarRateCard entity = this.getStarRateCard(dto.getId(), true);
        BusinessAssert.isTrue(ApprovalStatusEnum.anyMatch(entity.getStatus(),
                ApprovalStatusEnum.UN_COMMIT, ApprovalStatusEnum.REJECTED, ApprovalStatusEnum.CANCEL), McnErrorCode.DATA_OPERATION_NOT_SUPPORTED);
        StarRateCard updateEntity = new StarRateCard();
        updateEntity.setId(dto.getId());
        updateEntity.setStatus(ApprovalStatusEnum.UN_COMMIT.getCode());
        updateEntity.setCardName(dto.getCardName());
        updateEntity.setEffectiveStartDate(dto.getEffectiveStartDate());
        updateEntity.setEffectiveEndDate(dto.getEffectiveEndDate());
        updateEntity.setRemark(StringUtils.defaultString(dto.getRemark()));
        starRateCardMapper.updateById(updateEntity);
        return entity.getId();
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean saveAndFillInDetail(StarRateCardDetailFillInDTO dto) {
        StarRateCard starRateCard = this.getStarRateCard(dto.getCardId(), true);
        List<Long> configIdList = CommonUtil.defaultList(JSONObject.parseObject(starRateCard.getConfigIds(), new TypeReference<List<Long>>(){}));
        List<StarRateCardConfig> starRateCardConfigList = CommonUtil.defaultList(starRateCardConfigMapper.selectList(Wrappers.lambdaQuery(StarRateCardConfig.class)
                .in(StarRateCardConfig::getId, configIdList).eq(StarRateCardConfig::getIsDelete, NumberCst.ZERO)));
        Map<String, StarRateCardDetail> existedDetailMap = CommonUtil.defaultList(starRateCardDetailMapper.selectList(Wrappers.lambdaQuery(StarRateCardDetail.class)
                .eq(StarRateCardDetail::getCardId, starRateCard.getId()).in(StarRateCardDetail::getStarId, dto.getStarIds()).eq(StarRateCardDetail::getIsDelete, NumberCst.ZERO))
        ).stream().collect(Collectors.toMap(k -> StringUtils.joinWith(SeparatorCst.UNDERLINE, k.getConfigId() , k.getStarPlatformId()), v -> v));
        List<String> platformIds = starRateCardConfigList.stream().map(StarRateCardConfig::getPlatformId).distinct().collect(Collectors.toList());
        Map<String, List<StarPlatformInfo>> starPlatformInfoMap = CommonUtil.defaultList(starPlatformInfoMapper.selectList(Wrappers.lambdaQuery(StarPlatformInfo.class)
                .in(StarPlatformInfo::getStarId, dto.getStarIds())
                .in(CollectionUtils.isNotEmpty(platformIds), StarPlatformInfo::getPlatformId, platformIds)
                .eq(StarPlatformInfo::getPlatformStatus, NumberCst.ZERO)
                .eq(StarPlatformInfo::getIsDelete, NumberCst.ZERO))).stream().collect(Collectors.groupingBy(StarPlatformInfo::getPlatformId));
        List<StarRateCardDetail> addDetailList = new ArrayList<>();
        for(StarRateCardConfig starRateCardConfig : starRateCardConfigList) {
            List<StarPlatformInfo> starPlatformInfos = starPlatformInfoMap.get(starRateCardConfig.getPlatformId());
            if(CollectionUtils.isEmpty(starPlatformInfos)) {
                continue;
            }
            for(StarPlatformInfo starPlatformInfo : starPlatformInfos) {
                if(existedDetailMap.containsKey(StringUtils.joinWith(SeparatorCst.UNDERLINE, starRateCardConfig.getId() , starPlatformInfo.getId()))) {
                    continue;
                }
                StarRateCardDetail detail = new StarRateCardDetail();
                detail.setCardId(dto.getCardId());
                detail.setConfigId(starRateCardConfig.getId());
                detail.setStarId(starPlatformInfo.getStarId());
                detail.setStarPlatformId(starPlatformInfo.getId());
                detail.setConfirmStatus(NumberCst.ZERO);
                addDetailList.add(detail);
            }
        }
        if(CollectionUtils.isNotEmpty(addDetailList)) {
            return starRateCardDetailMapper.insertBatch(addDetailList) > 0;
        }
        return true;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean saveAndConfirmDetail(StarRateCardDetailSaveDTO dto) {
        StarRateCard starRateCard = this.getStarRateCard(dto.getCardId(), true);
        List<StarRateCardDetail> starRateCardDetailList = CommonUtil.defaultList(starRateCardDetailMapper.selectList(Wrappers.lambdaQuery(StarRateCardDetail.class)
                .eq(StarRateCardDetail::getCardId, starRateCard.getId())
                .in(StarRateCardDetail::getId, dto.getItems().stream().map(StarRateCardDetailSaveDTO.StarRateCardItem::getId).collect(Collectors.toList()))
                .eq(StarRateCardDetail::getIsDelete, NumberCst.ZERO)));
        if(CollectionUtils.isNotEmpty(starRateCardDetailList)) {
            Map<Long, StarRateCardDetail> starRateCardDetailMap = starRateCardDetailList.stream().collect(Collectors.toMap(StarRateCardDetail::getId, Function.identity()));
            List<Long> configIds = starRateCardDetailList.stream().map(StarRateCardDetail::getConfigId).collect(Collectors.toList());
            //Map<Long, String> configPlatformIdMap = CommonUtil.defaultList(starRateCardConfigMapper.selectBatchIds(configIds)).stream().collect(Collectors.toMap(StarRateCardConfig::getId, StarRateCardConfig::getPlatformId));
            Map<Long, List<StarQuoteWay>> starQuoteWayMap = this.getStarQuoteWayMap(configIds);
            for(StarRateCardDetailSaveDTO.StarRateCardItem item : dto.getItems()) {
                StarRateCardDetail starRateCardDetail = starRateCardDetailMap.get(item.getId());
                BusinessAssert.notNull(starRateCardDetail, McnErrorCode.DATA_NOT_FOUND,"该刊例明细不存在");
                if(NumberCst.ONE.equals(dto.getConfirmStatus())) {
                    starRateCardDetail.setQuoteWayData(item.getQuoteWayData());
                    starRateCardDetail.setQuoteInfoData(item.getQuoteInfoData());
                    this.verifyQuateWayIfConfirmed(item.getQuoteWayData(), starQuoteWayMap.get(starRateCardDetail.getConfigId()));
                    //this.verifyQuateInfoIfConfirmed(starRateCardDetail.getQuoteInfoData(), configPlatformIdMap.get(starRateCardDetail.getConfigId()));
                }
                StarRateCardDetail updateEntity = new StarRateCardDetail();
                updateEntity.setId(starRateCardDetail.getId());
                updateEntity.setQuoteWayData(item.getQuoteWayData());
                updateEntity.setQuoteInfoData(item.getQuoteInfoData());
                updateEntity.setConfirmStatus(dto.getConfirmStatus());
                starRateCardDetailMapper.updateById(updateEntity);
            }
        }
        return true;
    }

    @Override
    public StarRateCardDetailQueryVO queryDetails(StarRateCard starRateCard, StarRateCardDetailQueryDTO queryDTO) {
        StarRateCardConfig starRateCardConfig = starRateCardConfigService.getStarRateCardConfig(queryDTO.getConfigId(), true);
        List<String> basicInfoList = JSONObject.parseObject(starRateCardConfig.getBasicInfo(), new TypeReference<List<String>>() {});
        List<String> platformInfoList = JSONObject.parseObject(starRateCardConfig.getPlatformInfo(), new TypeReference<List<String>>() {});
        queryDTO.setCustomSql(CommonUtil.getCustomSql("b.", basicInfoList), CommonUtil.getCustomSql("d.", platformInfoList));
        Page<JSONObject> page = starRateCardMapper.queryDetails(new Page<>(queryDTO.getPageNum(), queryDTO.getPageSize(), queryDTO.getSearchCount().booleanValue()), queryDTO);
        StarRateCardDetailQueryVO detailQueryVO = new StarRateCardDetailQueryVO();
        detailQueryVO.addTableHeads(RateCardBasicInfoEnum.getTableHead(basicInfoList));
        detailQueryVO.addTableHeads(RateCardPlatformInfoEnum.getTableHead(platformInfoList));
        List<StarQuoteWay> starQuoteWays = CommonUtil.defaultList(starRateCardConfigService.getStarQuoteWays(Arrays.asList(queryDTO.getConfigId()), NumberCst.ONE));
        List<String> wayCodeList = new ArrayList<>(starQuoteWays.size());
        if(CollectionUtils.isNotEmpty(starQuoteWays)) {
            starQuoteWays.forEach(starQuoteWay -> {
                if(QuoteDataTypeEnum.isDict(starQuoteWay.getDataType())) {
                    wayCodeList.add(starQuoteWay.getWayCode());
                }
                detailQueryVO.addTableHead(new StarRateCardDetailQueryVO.TableHead(NumberCst.THREE, starQuoteWay.getWayCode(), starQuoteWay.getWayName(),
                        starQuoteWay.getDataType(), starQuoteWay.getRequired(), false,false, 300 + ObjectUtils.defaultIfNull(starQuoteWay.getOrderNum(), 0)));
            });
        }
        Map<String, Map<String, BaseSecondlevelDictsVO>> secondlevelDictMap = CommonUtil.defaultList(baseInfoClient.getSecondDictList(DictTypeCst.PUBLISHING_PLATFORM, false).getObj())
                .stream().collect(Collectors.toMap(k -> k.getDictsCode(), v -> CommonUtil.defaultList(v.getThirdDictList()).stream()
                        .sorted(Comparator.comparing(BaseSecondlevelDictsVO::getDictsStatus)).collect(LinkedHashMap::new, (n, o) -> n.putIfAbsent(o.getSecondlevelCode(), o), Map::putAll)));
        Map<String, BaseSecondlevelDictsVO> publishFormDictMap = secondlevelDictMap.get(starRateCardConfig.getPlatformId());
        if(MapUtils.isNotEmpty(publishFormDictMap)) {
            for(Map.Entry<String, BaseSecondlevelDictsVO> entry : publishFormDictMap.entrySet()) {
                BaseSecondlevelDictsVO secondlevelDict =  entry.getValue();
                if(ObjectUtils.defaultIfNull(secondlevelDict.getDictsStatus(), 0).intValue() == 0) {
                    detailQueryVO.addTableHead(new StarRateCardDetailQueryVO.TableHead(NumberCst.FOUR, entry.getKey(), secondlevelDict.getSecondlevelDictsNameC(), false,false,
                            400 + ObjectUtils.defaultIfNull(secondlevelDict.getSortNo(), 0)));
                }
            }
        }
        detailQueryVO.setBodyData(page);
        if(CollectionUtils.isNotEmpty(page.getRecords())) {
            fillAndTranslateData(page, starQuoteWays, wayCodeList, publishFormDictMap);
        }
        if(CollectionUtils.isNotEmpty(detailQueryVO.getBodyData().getRecords())) {
            detailQueryVO.getBodyData().getRecords().forEach(record -> {
                String starAddress = record.getString(RateCardBasicInfoEnum.STAR_ADDRESS.getFieldName());
                if(StringUtils.isNotBlank(starAddress)) {
                    record.put(RateCardBasicInfoEnum.STAR_ADDRESS.getFieldName(),
                            StringUtils.join(JSONObject.parseObject(starAddress, new TypeReference<List<String>>(){}), SeparatorCst.COMMA));
                }
            });
        }
        return detailQueryVO;
    }

    /**
     * 填充并翻译明细分页结果数据
     * @param page
     * @param starQuoteWays
     * @param wayCodeList
     * @param publishFormDictMap
     */
    private void fillAndTranslateData(Page<JSONObject> page, List<StarQuoteWay> starQuoteWays,  List<String> wayCodeList, Map<String, BaseSecondlevelDictsVO> publishFormDictMap) {
        List<String> dictCodeList = new NoRepeatableArrayList<>();
        page.getRecords().forEach(record -> {
            if(record.containsKey(RateCardBasicInfoEnum.GENDER.getFieldName())) {
                String sexFormat = record.getBoolean(RateCardBasicInfoEnum.GENDER.getFieldName()).booleanValue()?  "女" : "男";
                record.put(RateCardBasicInfoEnum.GENDER.getFieldName(), sexFormat);
            }
            if(record.containsKey(RateCardBasicInfoEnum.GOOD_AT_TYPE.getFieldName())) {
                dictCodeList.add(record.getString(RateCardBasicInfoEnum.GOOD_AT_TYPE.getFieldName()));
            }
            if(record.containsKey(RateCardBasicInfoEnum.MAIN_ACTIVE_PLATFORMS.getFieldName())) {
                dictCodeList.add(record.getString(RateCardBasicInfoEnum.MAIN_ACTIVE_PLATFORMS.getFieldName()));
            }
            if(record.containsKey(RateCardBasicInfoEnum.STAR_LABEL.getFieldName())) {
                dictCodeList.add(record.getString(RateCardBasicInfoEnum.STAR_LABEL.getFieldName()));
            }
            if(record.containsKey(RateCardBasicInfoEnum.STAR_CLASSIFICATION.getFieldName())) {
                dictCodeList.add(record.getString(RateCardBasicInfoEnum.STAR_CLASSIFICATION.getFieldName()));
            }
            //自定义信息
            if(CollectionUtils.isNotEmpty(starQuoteWays)) {
                JSONObject jsonObject = CommonUtil.defaultParseObject(record.getString("quote_way_data"));
                starQuoteWays.forEach(starQuoteWay -> {
                    if(jsonObject.containsKey(starQuoteWay.getWayCode())) {
                        record.put(starQuoteWay.getWayCode(), jsonObject.getString(starQuoteWay.getWayCode()));
                    } else {
                        record.put(starQuoteWay.getWayCode(), null);
                    }
                });
                wayCodeList.forEach(wayCode -> {
                    if(jsonObject.containsKey(wayCode)) {
                        dictCodeList.add(jsonObject.getString(wayCode));
                    }
                });
            }
            //报价信息
            if(MapUtils.isNotEmpty(publishFormDictMap)) {
                JSONObject jsonObject = CommonUtil.defaultParseObject(record.getString("quote_info_data"));
                for(Map.Entry<String, BaseSecondlevelDictsVO> entry : publishFormDictMap.entrySet()) {
                    if(jsonObject.containsKey(entry.getKey())) {
                        record.put(entry.getKey(), jsonObject.getString(entry.getKey()));
                    } else {
                        record.put(entry.getKey(), null);
                    }
                }
            }
        });
        //字典翻译
        if(CollectionUtils.isEmpty(dictCodeList)) {
            return;
        }
        List<String> profileCodes = Arrays.asList(DictTypeCst.GOOD_AT_TYPE, DictTypeCst.ACTIVE_PLATFORMS,
                DictTypeCst.STAR_LABEL, DictTypeCst.STAR_CLASSIFICATION, DictTypeCst.STAR_SPACIAL_TAG);
        Map<String, String> dictsNameMap = this.searchSomething(profileCodes, dictCodeList);
        if(MapUtils.isEmpty(dictsNameMap)) {
            return;
        }
        for(JSONObject record : page.getRecords()) {
            if(record.containsKey(RateCardBasicInfoEnum.GOOD_AT_TYPE.getFieldName())) {
                record.put(RateCardBasicInfoEnum.GOOD_AT_TYPE.getFieldName(), this.getDictValues(record.getString(RateCardBasicInfoEnum.GOOD_AT_TYPE.getFieldName()), dictsNameMap, DictTypeCst.GOOD_AT_TYPE));
            }
            if(record.containsKey(RateCardBasicInfoEnum.MAIN_ACTIVE_PLATFORMS.getFieldName())) {
                record.put(RateCardBasicInfoEnum.MAIN_ACTIVE_PLATFORMS.getFieldName(), this.getDictValues(record.getString(RateCardBasicInfoEnum.MAIN_ACTIVE_PLATFORMS.getFieldName()), dictsNameMap, DictTypeCst.ACTIVE_PLATFORMS));
            }
            if(record.containsKey(RateCardBasicInfoEnum.STAR_LABEL.getFieldName())) {
                record.put(RateCardBasicInfoEnum.STAR_LABEL.getFieldName(), this.getDictValues(record.getString(RateCardBasicInfoEnum.STAR_LABEL.getFieldName()), dictsNameMap, DictTypeCst.STAR_LABEL));
            }
            if(record.containsKey(RateCardBasicInfoEnum.STAR_CLASSIFICATION.getFieldName())) {
                record.put(RateCardBasicInfoEnum.STAR_CLASSIFICATION.getFieldName(), this.getDictValues(record.getString(RateCardBasicInfoEnum.STAR_CLASSIFICATION.getFieldName()), dictsNameMap, DictTypeCst.STAR_CLASSIFICATION));
            }
            String quoteWayData = record.getString("quote_way_data");
            if(StringUtils.isNotBlank(quoteWayData)) {
                JSONObject jsonObject = JSONObject.parseObject(quoteWayData);
                wayCodeList.forEach(wayCode -> {
                    if(jsonObject.containsKey(wayCode)) {
                        jsonObject.put(wayCode, dictsNameMap.get(DictTypeCst.STAR_SPACIAL_TAG + SeparatorCst.SEMICOLON + jsonObject.getString(wayCode)));
                    }
                });
                record.putAll(jsonObject);
            }
        }
    }

    @Override
    public Page<StarRateCardListVO> queryByPage(StarRateCardSearchDTO searchDTO) {
        Page<StarRateCardListVO> page = new Page<>(searchDTO.getPageNum(), searchDTO.getPageSize());
        searchDTO.setAlias("sr.");
        searchDTO.setFieldName("createDate");
        List<Long> processIds = Arrays.asList(ApprovalEnum.STAR_RATE_CARD.getProcessId());
        if (CollectionUtils.isNotEmpty(searchDTO.getFlowStatus())) {
            List<Long> allList = approvalFlowService.getSrcList(searchDTO.getFlowStatus(), processIds);
            if (CollectionUtils.isEmpty(allList)) {
                return page;
            }
            searchDTO.setSrcIds(allList);
        }
        Page<StarRateCardListVO> pageList = starRateCardMapper.selectByPage(page, searchDTO);
        List<StarRateCardListVO> records = pageList.getRecords();
        if (CollectionUtils.isNotEmpty(records)) {
            //查询FlowId
            List<Long> idList = records.stream().map(StarRateCardListVO::getId).collect(Collectors.toList());
            Map<Long, Long> flowMap = approvalFlowService.getNewestFlowIds(idList, processIds);
            if (MapUtils.isNotEmpty(flowMap)) {
                records.forEach(record -> {
                    record.setFlowId(flowMap.get(record.getId()));
                });
                //审批名称
                List<Long> flowList = records.stream().map(StarRateCardListVO::getFlowId).filter(Objects::nonNull).collect(Collectors.toList());
                JSONObject flowMapping = approvalFlowService.getFlowStatus(flowList);
                if (MapUtils.isNotEmpty(flowMapping)) {
                    records.forEach(record -> {
                        record.setFlowStatusName(CommonUtil.defaultString(flowMapping, record.getFlowId()));
                    });
                }
            }
        }
        return pageList;
    }

    @Override
    public StarRateCardInfoVO detail(Long id) {
        StarRateCard entity = this.getStarRateCard(id, true);
        StarRateCardInfoVO vo = new StarRateCardInfoVO();
        BeanUtils.copyProperties(entity, vo);
        //审批流信息
        Long flowId = approvalFlowService.getFlowId(id, ApprovalEnum.STAR_RATE_CARD.getProcessId());
        String stringFlowIds = approvalFlowService.getStringFlowIds(id, ApprovalEnum.STAR_RATE_CARD.getProcessId());
        vo.setFlowId(flowId);
        vo.setFlowIds(stringFlowIds);
        if(Objects.nonNull(flowId)) {
            List<Long> flowList = new ArrayList<>(Collections.singletonList(flowId));
            JSONObject flowMapping = approvalFlowService.getFlowStatus(flowList);
            if (Objects.nonNull(flowMapping)) {
                if (flowMapping.size() > 0 && Objects.nonNull(vo.getFlowId())) {
                    vo.setFlowStatusName(flowMapping.getString(vo.getFlowId().toString()));
                }
            }
        }
        vo.setIsExisted(starRateCardDetailMapper.exist(Wrappers.lambdaQuery(StarRateCardDetail.class)
                .eq(StarRateCardDetail::getCardId, id).eq(StarRateCardDetail::getConfirmStatus, NumberCst.ZERO)
                .eq(StarRateCardDetail::getIsDelete, NumberCst.ZERO)) ? NumberCst.ONE : NumberCst.ZERO);
        return vo;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean changeStatus(Long id, Integer approvalStatus) {
        StarRateCard entity = this.getStarRateCard(id, true);
        BusinessAssert.isFalse(ApprovalStatusEnum.isInvalid(entity.getStatus()), McnErrorCode.BUSINESS_ERROR, "该刊例已作废！");
        if (ApprovalStatusEnum.isApproval(approvalStatus)) {
            BusinessAssert.isTrue(ApprovalStatusEnum.anyMatch(entity.getStatus(),
                    ApprovalStatusEnum.UN_COMMIT, ApprovalStatusEnum.CANCEL, ApprovalStatusEnum.REJECTED),
                    McnErrorCode.BUSINESS_ERROR, "当前刊例状态-不允许重新提交审批！");
            BusinessAssert.isTrue(starRateCardDetailMapper.notExist(Wrappers.lambdaQuery(StarRateCardDetail.class)
                            .eq(StarRateCardDetail::getCardId, id).eq(StarRateCardDetail::getConfirmStatus, NumberCst.ZERO)
                            .eq(StarRateCardDetail::getIsDelete, NumberCst.ZERO)),
                    McnErrorCode.BUSINESS_ERROR, "请先将红人报价信息状态全部更新为【已确认】！");
        }
        StarRateCard updateEntity = new StarRateCard();
        updateEntity.setId(entity.getId());
        updateEntity.setStatus(approvalStatus);
        boolean result = starRateCardMapper.updateById(updateEntity) > 0;
        if (result) {
            //如果刊例撤回或作废，则撤回已提起的审批流程
            if (ApprovalStatusEnum.anyMatch(approvalStatus, ApprovalStatusEnum.CANCEL, ApprovalStatusEnum.INVALID)) {
                approvalFlowService.revokeProcess(entity.getId(), ApprovalEnum.STAR_RATE_CARD.getProcessId());
            } else if (StarRebateStatusEnum.isApproval(approvalStatus)) {
                approvalFlowService.startProcess(entity.getId(), entity.getCardCode(), ApprovalEnum.STAR_RATE_CARD, JSON.toJSONString(entity));
            }
        }
        return result;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean confirmUpdate(StarRateCardConfirmUpdateDTO dto) {
        StarRateCard entity = this.getStarRateCard(dto.getId(), true);
        List<StarRateCardDetail> starRateCardDetailList = starRateCardDetailMapper.selectList(Wrappers.lambdaQuery(StarRateCardDetail.class)
                .eq(StarRateCardDetail::getCardId, entity.getId())
                .in(CollectionUtils.isNotEmpty(dto.getDetailIds()), StarRateCardDetail::getId, dto.getDetailIds())
                .eq(StarRateCardDetail::getConfirmStatus, NumberCst.ZERO)
                .eq(StarRateCardDetail::getIsDelete, NumberCst.ZERO));
        if(CollectionUtils.isNotEmpty(starRateCardDetailList)) {
            List<Long> configIds = starRateCardDetailList.stream().map(StarRateCardDetail::getConfigId).collect(Collectors.toList());
            //Map<Long, String> configPlatformIdMap = CommonUtil.defaultList(starRateCardConfigMapper.selectBatchIds(configIds)).stream().collect(Collectors.toMap(StarRateCardConfig::getId, StarRateCardConfig::getPlatformId));
            Map<Long, List<StarQuoteWay>> starQuoteWayMap = this.getStarQuoteWayMap(configIds);
            starRateCardDetailList.forEach(detail -> {
                this.verifyQuateWayIfConfirmed(detail.getQuoteWayData(), starQuoteWayMap.get(detail.getConfigId()));
                //this.verifyQuateInfoIfConfirmed(detail.getQuoteInfoData(), configPlatformIdMap.get(detail.getConfigId()));
            });
            List<Long> detailIds = starRateCardDetailList.stream().map(StarRateCardDetail::getId).collect(Collectors.toList());
            starRateCardDetailMapper.updateByNoStrategy(Wrappers.lambdaUpdate(StarRateCardDetail.class)
                    .set(StarRateCardDetail::getConfirmStatus, NumberCst.ONE)
                    .set(StarRateCardDetail::getUpdateBy, UserVoContextHolder.getUserId())
                    .set(StarRateCardDetail::getUpdateName, UserVoContextHolder.getUserName())
                    .set(StarRateCardDetail::getUpdateDate, new Date())
                    .in(StarRateCardDetail::getId, detailIds)
                    .eq(StarRateCardDetail::getConfirmStatus, NumberCst.ZERO)
                    .eq(StarRateCardDetail::getIsDelete, NumberCst.ZERO));
        }
        return true;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean copyHistory(Long id) {
        StarRateCard targetStarRateCard = this.getStarRateCard(id, true);
        StarRateCard sourceStarRateCard = starRateCardMapper.selectOne(Wrappers.lambdaQuery(StarRateCard.class)
                .eq(StarRateCard::getIsDelete, NumberCst.ZERO).ne(StarRateCard::getStatus, ApprovalStatusEnum.INVALID.getCode())
                .ne(StarRateCard::getId, id).orderByDesc(StarRateCard::getCreateDate).last(" limit 1"));
        BusinessAssert.notNull(sourceStarRateCard, McnErrorCode.DATA_NOT_FOUND,"未找到非作废的历史刊例");
        List<StarRateCardDetail> sourceDetailList = starRateCardDetailMapper.selectCopyDetail(sourceStarRateCard.getId());
        BusinessAssert.notEmpty(sourceDetailList, McnErrorCode.DATA_NOT_FOUND,"历史刊例无数据");
        Map<Long, List<StarRateCardDetail>> sourceDetailMap = sourceDetailList.stream().collect(Collectors.groupingBy(StarRateCardDetail::getConfigId));
        List<StarRateCardDetail> targetDetailList = new ArrayList<>(sourceDetailList.size());
        //
        List<Long> configIdList = JSONObject.parseObject(targetStarRateCard.getConfigIds(), new TypeReference<List<Long>>(){});
        Map<String, Long> platformConfigIdMap = this.getPlatforConfigIdMap(configIdList);
        List<StarQuoteWay> starQuoteWayList = starRateCardConfigService.getStarQuoteWays(configIdList, NumberCst.ONE);
        Map<Long, List<StarQuoteWay>> starQuoteWayMap = starQuoteWayList.stream().collect(Collectors.groupingBy(StarQuoteWay::getConfigId));
        for(Map.Entry<Long, List<StarRateCardDetail>> entry : sourceDetailMap.entrySet()) {
            StarRateCardConfig starRateCardConfig = starRateCardConfigMapper.selectById(entry.getKey());
            if(!platformConfigIdMap.containsKey(starRateCardConfig.getPlatformId())) {
                continue;
            }
            Long configId = platformConfigIdMap.get(starRateCardConfig.getPlatformId());
            List<StarQuoteWay> starQuoteWays = starQuoteWayMap.get(configId);
            entry.getValue().forEach(detail -> {
                detail.setId(null);
                detail.setCardId(id);
                detail.setConfigId(configId);
                if(StringUtils.isNotBlank(detail.getQuoteWayData()) && CollectionUtils.isNotEmpty(starQuoteWays)) {
                    JSONObject sourceJsonObject = JSONObject.parseObject(detail.getQuoteWayData());
                    JSONObject targetJsonObject = new JSONObject(starQuoteWays.size());
                    starQuoteWays.forEach(starQuoteWay -> {
                        if(sourceJsonObject.containsKey(starQuoteWay.getWayCode())) {
                            targetJsonObject.put(starQuoteWay.getWayCode(), sourceJsonObject.get(starQuoteWay.getWayCode()));
                        }
                    });
                    if(targetJsonObject.size() > 0) {
                        detail.setQuoteWayData(targetJsonObject.toJSONString());
                    }
                }
                detail.setConfirmStatus(NumberCst.ZERO);
            });
            targetDetailList.addAll(entry.getValue());
        }
        if(CollectionUtils.isNotEmpty(targetDetailList)) {
            starRateCardDetailMapper.updateByNoStrategy(Wrappers.lambdaUpdate(StarRateCardDetail.class)
                    .set(StarRateCardDetail::getIsDelete, NumberCst.ONE)
                    .set(StarRateCardDetail::getUpdateBy, UserVoContextHolder.getUserId())
                    .set(StarRateCardDetail::getUpdateName, UserVoContextHolder.getUserName())
                    .set(StarRateCardDetail::getUpdateDate, new Date())
                    .eq(StarRateCardDetail::getCardId, id)
                    .eq(StarRateCardDetail::getIsDelete, NumberCst.ZERO));
            starRateCardDetailMapper.insertBatch(targetDetailList);
        }
        return true;
    }

    @Override
    public boolean deleteDetails(StarRateCardDetailDeleteDTO dto) {
        StarRateCard entity = this.getStarRateCard(dto.getId(), true);
        return starRateCardDetailMapper.updateByNoStrategy(Wrappers.lambdaUpdate(StarRateCardDetail.class)
                .set(StarRateCardDetail::getIsDelete, NumberCst.ONE)
                .set(StarRateCardDetail::getUpdateBy, UserVoContextHolder.getUserId())
                .set(StarRateCardDetail::getUpdateName, UserVoContextHolder.getUserName())
                .set(StarRateCardDetail::getUpdateDate, new Date())
                .eq(StarRateCardDetail::getCardId, entity.getId())
                .in(StarRateCardDetail::getId, dto.getDetailIds()));
    }

    @Override
    public List<StarRateCardPlatformTabVO> getPlatformTab(Long id) {
        StarRateCard entity = this.getStarRateCard(id, true);
        List<Long> configIdList = JSONObject.parseObject(entity.getConfigIds(), new TypeReference<List<Long>>(){});
        if(CollectionUtils.isNotEmpty(configIdList)) {
            List<StarQuoteWay> starQuoteWays = CommonUtil.defaultList(starRateCardConfigService.getStarQuoteWays(configIdList, NumberCst.ONE));
            Map<Long, List<StarQuoteWay>> configMap = starQuoteWays.stream().collect(Collectors.groupingBy(StarQuoteWay::getConfigId));
            List<StarRateCardConfig> dataList = starRateCardConfigMapper.selectList(Wrappers.lambdaQuery(StarRateCardConfig.class)
                    .in(StarRateCardConfig::getId, configIdList).eq(StarRateCardConfig::getIsDelete, NumberCst.ZERO).orderByAsc(StarRateCardConfig::getPlatformId));
            if(CollectionUtils.isNotEmpty(dataList)) {
                return dataList.stream().map(e -> {
                    StarRateCardPlatformTabVO target = new StarRateCardPlatformTabVO();
                    target.setConfigId(e.getId());
                    if(configMap.containsKey(e.getId())) {
                        target.setStarQuoteWayList(configMap.get(e.getId()).stream().map(i -> {
                            StarQuoteWayVO starQuoteWayVO = new StarQuoteWayVO();
                            BeanUtils.copyProperties(i, starQuoteWayVO);
                            return starQuoteWayVO;
                        }).collect(Collectors.toList()));
                    }
                    target.setPlatformId(e.getPlatformId());
                    target.setPlatformName(e.getPlatformName());
                    return target;
                }).collect(Collectors.toList());
            }
        }
        return null;
    }

    @Override
    public List<Long> getAllStarId(Long cardId) {
        List<StarRateCardDetail> detailList = starRateCardDetailMapper.selectList(Wrappers.lambdaQuery(StarRateCardDetail.class)
                .eq(StarRateCardDetail::getCardId, cardId).eq(StarRateCardDetail::getIsDelete, NumberCst.ZERO));
        if(CollectionUtils.isEmpty(detailList)) {
            return null;
        }
        return detailList.stream().map(StarRateCardDetail::getStarId).distinct().collect(Collectors.toList());
    }

    /**
     * 根据刊例ID获取刊例信息
     * @param id
     * @param verifyExist
     * @return
     */
    @Override
    public StarRateCard getStarRateCard(Long id, boolean verifyExist) {
        BusinessAssert.notNull(id,"刊例ID不能为空");
        StarRateCard entity = starRateCardMapper.selectOne(Wrappers.lambdaQuery(StarRateCard.class)
                .eq(StarRateCard::getId, id).eq(StarRateCard::getIsDelete, NumberCst.ZERO));
        if(verifyExist) {
            BusinessAssert.notNull(entity, McnErrorCode.DATA_NOT_FOUND,"该刊例不存在");
        }
        return entity;
    }

    @Override
    public List<DynamicExcel> getDynamicExcelList(StarRateCard starRateCard) {
        List<Long> configIdList = CommonUtil.defaultList(JSONObject.parseObject(starRateCard.getConfigIds(), new TypeReference<List<Long>>(){}));
        List<StarRateCardConfig> configList = starRateCardConfigMapper.selectList(Wrappers.lambdaQuery(StarRateCardConfig.class)
                .in(StarRateCardConfig::getId, configIdList).eq(StarRateCardConfig::getIsDelete, NumberCst.ZERO).orderByAsc(StarRateCardConfig::getPlatformId));
        List<DynamicExcel> dynamicExcelList = new ArrayList<>(configList.size());
        Map<String, byte[]> iamgeMap = new HashMap<>(16);
        for(StarRateCardConfig config : configList) {
            StarRateCardDetailQueryDTO searchDTO = new StarRateCardDetailQueryDTO();
            //不分页
            searchDTO.setPageSize(-1L);
            //不统计总数
            searchDTO.setSearchCount(false);
            searchDTO.setId(starRateCard.getId());
            searchDTO.setConfigId(config.getId());
            StarRateCardDetailQueryVO queryVO = this.queryDetails(starRateCard, searchDTO);
            DynamicExcel dynamicExcel = new DynamicExcel(config.getPlatformName(), queryVO.getEntityList(), queryVO.getDataSet(webImagePrefix, iamgeMap));
            dynamicExcelList.add(dynamicExcel);
        }
        return dynamicExcelList;
    }

    @Override
    public Page<RateCardStarPlatformVO> getStarPlatformList(RateCardStarPlatformQueryDTO queryDTO) {
        return starRateCardMapper.getStarPlatformList(new Page<>(queryDTO.getPageNum(), queryDTO.getPageSize()), queryDTO);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean addStarPlatform(StarRateCardDetailAddPlatformDTO dto) {
        StarRateCard starRateCard = this.getStarRateCard(dto.getCardId(), true);
        List<Long> configIdList = CommonUtil.defaultList(JSONObject.parseObject(starRateCard.getConfigIds(), new TypeReference<List<Long>>(){}));
        BusinessAssert.isTrue(configIdList.contains(dto.getConfigId()), McnErrorCode.PARAM_ERROR, "本刊例未关联此刊例配置！");
        StarRateCardConfig starRateCardConfig = starRateCardConfigService.getStarRateCardConfig(dto.getConfigId(), true);
        List<Long> starPlatformInfoIds = CommonUtil.defaultList(dto.getStarPlatformInfoIds()).stream().filter(e -> Objects.nonNull(e)).distinct().collect(Collectors.toList());
        BusinessAssert.notEmpty(starPlatformInfoIds, McnErrorCode.PARAM_ERROR, "红人平台信息ID集合不能为空");
        Map<Long, StarPlatformInfo> starPlatformInfoMap = CommonUtil.defaultList(starPlatformInfoMapper.selectList(Wrappers.lambdaQuery(StarPlatformInfo.class)
                .in(StarPlatformInfo::getId, starPlatformInfoIds)
                .in(StarPlatformInfo::getPlatformId, starRateCardConfig.getPlatformId())
                .eq(StarPlatformInfo::getPlatformStatus, NumberCst.ZERO)
                .eq(StarPlatformInfo::getIsDelete, NumberCst.ZERO))).stream().collect(Collectors.toMap(k -> k.getId(), v -> v));
        BusinessAssert.notEmpty(starPlatformInfoMap, McnErrorCode.PARAM_ERROR, "查询不到要添加的红人平台信息！");
        List<StarRateCardDetail> addDetailList = new ArrayList<>(starPlatformInfoIds.size());
        Map<String, StarRateCardDetail> existedDetailMap = CommonUtil.defaultList(starRateCardDetailMapper.selectList(Wrappers.lambdaQuery(StarRateCardDetail.class)
                .eq(StarRateCardDetail::getCardId, starRateCard.getId()).eq(StarRateCardDetail::getConfigId, starRateCardConfig.getId())
                .in(StarRateCardDetail::getStarPlatformId, starPlatformInfoIds).eq(StarRateCardDetail::getIsDelete, NumberCst.ZERO))
        ).stream().collect(Collectors.toMap(k -> StringUtils.joinWith(SeparatorCst.UNDERLINE, k.getConfigId() , k.getStarPlatformId()), v -> v));
        starPlatformInfoIds.forEach(starPlatformInfoId -> {
            BusinessAssert.isTrue(starPlatformInfoMap.containsKey(starPlatformInfoId), McnErrorCode.PARAM_ERROR, "查询不到此红人平台信息！ID="+starPlatformInfoId);
            StarPlatformInfo starPlatformInfo = starPlatformInfoMap.get(starPlatformInfoId);
            String existedKey = StringUtils.joinWith(SeparatorCst.UNDERLINE, starRateCardConfig.getId() , starPlatformInfoId);
            if(existedDetailMap.containsKey(existedKey)) {
                throw new BusinessException(McnErrorCode.PARAM_ERROR.code(),
                        String.format("【%s】%s-平台已存在，添加失败！", starPlatformInfo.getPlatNickName(), starPlatformInfo.getPlatformName()));
            }
            StarRateCardDetail detail = new StarRateCardDetail();
            detail.setCardId(starRateCard.getId());
            detail.setConfigId(starRateCardConfig.getId());
            detail.setStarId(starPlatformInfo.getStarId());
            detail.setStarPlatformId(starPlatformInfo.getId());
            detail.setConfirmStatus(NumberCst.ZERO);
            addDetailList.add(detail);
        });
        return starRateCardDetailMapper.insertBatch(addDetailList) > 0;
    }

    @Override
    public boolean remindAgent(StarRateCardRemindDTO dto) {
        StarRateCard starRateCard = this.getStarRateCard(dto.getId(), true);
        List<StarRateCardDetail> detailList = starRateCardDetailMapper.selectList(Wrappers.lambdaQuery(StarRateCardDetail.class)
                .eq(StarRateCardDetail::getCardId, starRateCard.getId())
                .in(StarRateCardDetail::getId, dto.getDetailIds())
                .eq(StarRateCardDetail::getConfirmStatus, NumberCst.ZERO)
                .eq(StarRateCardDetail::getIsDelete, NumberCst.ZERO));
        BusinessAssert.isTrue(CollectionUtils.size(detailList) == dto.getDetailIds().size(), McnErrorCode.BUSINESS_ERROR, "请勾选【未确认】状态的刊例！");
        List<Long> starIds = detailList.stream().map(StarRateCardDetail::getStarId).distinct().collect(Collectors.toList());
        List<StarBasicData> starBasicDataList = starBasicDataMapper.selectBatchIds(starIds);
        starBasicDataList.forEach(e -> {
            MessagePlanVo planVo = new MessagePlanVo();
            planVo.setTenantId(ObjectUtils.defaultIfNull(UserVoContextHolder.getTenantId(), e.getTenantId()));
            planVo.setMessageKey(CommonUtil.getMessageKey(null));
            planVo.setJumpUrlType("page");
            planVo.setTemplate(MsgCenterTemplateCst.RATECARD_REMIND_AGENT);
            MessageDetailVo messageDetailVo = new MessageDetailVo();
            messageDetailVo.setUserId(e.getStarAgentId());
            Map<String, String> externParams = new HashMap<>(4);
            externParams.put("url", "advertisementManageDetail");
            externParams.put("businessId", starRateCard.getId().toString());
            externParams.put("name", starRateCard.getCardName());
            externParams.put("code", starRateCard.getCardCode());
            messageDetailVo.setExternParams(externParams);
            planVo.setDetails(Arrays.asList(messageDetailVo));
            messageClient.sendMessage(planVo);
        });
        return true;
    }

    @Override
    public BigDecimal getRateCardPrice(RateCardPriceQueryDTO dto) {
        StarPlatformInfo starPlatformInfo = starPlatformInfoMapper.selectById(dto.getStarPlatformId());
        BusinessAssert.notNull(starPlatformInfo, "红人平台信息不存在！无法获取最新刊例报价");
        dto.setPublishDate(DateUtil.getLocalDate(dto.getPublishDate()));
        Map<String, String> secondlevelDictsMap = baseInfoClient.getEnabledSecondlevelDictMap(DictTypeCst.PUBLISHING_PLATFORM).get(starPlatformInfo.getPlatformId());
        BusinessAssert.isTrue(secondlevelDictsMap.containsKey(dto.getPublishForm()), "发布形式不存在或已禁用！无法获取最新刊例报价");
        String rateCardPrice = starRateCardMapper.getRateCardPrice(dto);
        log.info("getRateCardPrice{}", rateCardPrice);
        if (rateCardPrice == null || StringUtils.isBlank(rateCardPrice)) {
            return null;
        }
        return new BigDecimal(rateCardPrice);
    }

    @Override
    public String importExcelById(StarRateCard starRateCard, Map<String, Map<Integer, String>> sheetHeaderMap, Map<String, List<Map<Integer, String>>> sheetBodyMap) {
        List<Long> configIdList = JSONObject.parseObject(starRateCard.getConfigIds(), new TypeReference<List<Long>>(){});
        List<StarRateCardConfig> configList = starRateCardConfigMapper.selectList(Wrappers.lambdaQuery(StarRateCardConfig.class)
                .in(StarRateCardConfig::getId, configIdList)
                .eq(StarRateCardConfig::getIsDelete, NumberCst.ZERO)
                .orderByAsc(StarRateCardConfig::getPlatformId));
        BusinessAssert.notEmpty(configList, "该刊例查询不到模板配置！");
        Map<String, StarRateCardConfig> configMap = configList.stream().collect(Collectors.toMap(StarRateCardConfig::getPlatformName, v -> v));
        //校验参数
        for(Map.Entry<String, StarRateCardConfig> entry : configMap.entrySet()) {
            String platformName = entry.getKey();
            if(!CommonUtil.containsKey(sheetHeaderMap, platformName)) {
                throw new BusinessException(McnErrorCode.BUSINESS_ERROR.code(), String.format("模板异常：【%s】数据缺失", platformName));
            }
            Map<Integer, String> headerMap = sheetHeaderMap.get(platformName);
            if(MapUtils.isEmpty(headerMap)) {
                throw new BusinessException(McnErrorCode.BUSINESS_ERROR.code(), String.format("模板异常：【%s】表头不能为空 ", platformName));
            }
            //判断空列
            boolean existNullCell = headerMap.entrySet().stream().anyMatch(e -> StringUtils.isBlank(e.getValue()));
            if(existNullCell) {
                throw new BusinessException(McnErrorCode.BUSINESS_ERROR.code(), String.format("模板异常：【%s】表头不能为空 ", platformName));
            }
            //识别昵称
            Integer nickNameIndex = headerMap.entrySet().stream().filter(e -> StringUtils.equals(e.getValue(), "昵称")).map(Map.Entry::getKey).findFirst().orElse(null);
            if(Objects.isNull(nickNameIndex) || nickNameIndex.intValue() != 0) {
                throw new BusinessException(McnErrorCode.BUSINESS_ERROR.code(), String.format("模板异常：【%s】表头「昵称」必须在第1列", platformName));
            }
            //重复表头判断
            List<String> repeatedHeadList = headerMap.entrySet().stream().map(Map.Entry::getValue).collect(Collectors.groupingBy(e -> StringUtils.lowerCase(e), Collectors.counting()))
                    .entrySet().stream().filter(e -> e.getValue().longValue() > 1L).map(Map.Entry::getKey).collect(Collectors.toList());
            if(CollectionUtils.isNotEmpty(repeatedHeadList)) {
                throw new BusinessException(McnErrorCode.BUSINESS_ERROR.code(), String.format("模板异常：【%s】重复表头 %s", platformName, StringUtils.joinWith(",", repeatedHeadList)));
            }
        }
        Map<Long, List<StarQuoteWay>> sqwAllMap = CommonUtil.defaultList(starRateCardConfigService.getStarQuoteWays(configIdList, null))
                .stream().collect(Collectors.groupingBy(StarQuoteWay::getConfigId));
        List<BaseDictsVO> dictList = baseInfoClient.getSecondDictList(DictTypeCst.PUBLISHING_PLATFORM, false).getObj();
        Map<String, Map<String, BaseSecondlevelDictsVO>> publishPlatromMap = CommonUtil.defaultList(dictList).stream().collect(Collectors.toMap(k -> k.getDictsCode(), v ->
                CommonUtil.defaultList(v.getThirdDictList()).stream().sorted(Comparator.comparing(BaseSecondlevelDictsVO::getDictsStatus))
                        .collect(LinkedHashMap::new, (n, o) -> n.putIfAbsent(StringUtils.lowerCase(o.getSecondlevelDictsNameC()), o), Map::putAll)
        ));
        List<String> platformIds = configList.stream().map(StarRateCardConfig::getPlatformId).distinct().collect(Collectors.toList());
        Map<String, List<StarPlatformInfo>> starPlatformInfoMap = CommonUtil.defaultList(
                starPlatformInfoMapper.getAllImportPlatformForImport(starRateCard.getTenantId(), platformIds))
                .stream().collect(Collectors.groupingBy(StarPlatformInfo::getPlatformId));
        List<StarRateCardDetail> detailList = new ArrayList<>();
        Map<String, Map<Integer, List<String>>> failReasonMap = new LinkedHashMap<>();
        for(Map.Entry<String, StarRateCardConfig> entry : configMap.entrySet()) {
            StarRateCardConfig starRateCardConfig = entry.getValue();
            String platformId = starRateCardConfig.getPlatformId();
            String platformName = entry.getKey();
            Map<Integer, String> headerMap = sheetHeaderMap.get(entry.getKey());
            if(!CommonUtil.containsKey(starPlatformInfoMap, platformId)) {
                log.error("查询不到红人平台信息：{}", platformName);
                continue;
            }
            if(!CommonUtil.containsKey(sheetBodyMap, platformName) || CollectionUtils.isEmpty(sheetBodyMap.get(platformName))) {
                log.error("Excel无数据，红人平台：{}", platformName);
                continue;
            }
            List<Map<Integer, String>> bodyList = sheetBodyMap.get(platformName);
            Map<String, StarQuoteWay> sqwMap = CommonUtil.defaultList(sqwAllMap.get(starRateCardConfig.getId())).stream().collect(Collectors.toMap(e -> StringUtils.lowerCase(e.getWayName()), Function.identity()));
            Map<String, BaseSecondlevelDictsVO> publishFormMap = publishPlatromMap.get(platformId);
            //不区分大小写
            Map<String, List<StarPlatformInfo>> platNickNameMap = CommonUtil.defaultList(starPlatformInfoMap.get(platformId)).stream().collect(Collectors.groupingBy(e -> StringUtils.lowerCase(e.getPlatNickName())));
            Set<String> platNickNameSet = new HashSet<>();
            Map<Integer, List<String>> rowErrorMap = new LinkedHashMap<>();
            for (int i = 0; i < bodyList.size(); i++) {
                Map<Integer, String> dataMap = bodyList.get(i);
                StarRateCardDetail starRateCardDetail = new StarRateCardDetail();
                starRateCardDetail.setCardId(starRateCard.getId());
                starRateCardDetail.setConfigId(starRateCardConfig.getId());
                starRateCardDetail.setConfirmStatus(NumberCst.ZERO);
                starRateCardDetail.setIsDelete(NumberCst.ZERO);
                List<String> errorList = new ArrayList<>();
                //自定义信息
                JSONObject qwdJSONObject = new JSONObject();
                //发布形式
                JSONObject qidJSONObject = new JSONObject();
                dataMap.entrySet().forEach(dataEntry -> {
                    String cellName = headerMap.get(dataEntry.getKey());
                    String cellValue = StringUtils.trim(dataEntry.getValue());
                    if(StringUtils.equals("昵称", cellName)) {
                        if(StringUtils.isBlank(cellValue)) {
                            errorList.add(String.format("【%s】必填", cellName));
                        } else if(platNickNameSet.contains(StringUtils.lowerCase(cellValue))) {
                            errorList.add(String.format("【%s】重复", cellName));
                        } else if(!CommonUtil.containsKey(platNickNameMap, StringUtils.lowerCase(cellValue))) {
                            errorList.add(String.format("【%s】系统未匹配到该昵称「%s」", cellName, cellValue));
                        } else {
                            platNickNameSet.add(StringUtils.lowerCase(cellValue));
                            StarPlatformInfo starPlatformInfo = platNickNameMap.get(StringUtils.lowerCase(cellValue)).stream().sorted(Comparator.comparing(StarPlatformInfo::getPlatformStatus)).findFirst().get();
                            starRateCardDetail.setStarPlatformId(starPlatformInfo.getId());
                            starRateCardDetail.setStarId(starPlatformInfo.getStarId());
                        }
                    } else {
                        if(StringUtils.isBlank(cellName)) {
                            errorList.add(String.format("【第%s列】表头不能为空", dataEntry.getKey()));
                        } else if(CommonUtil.containsKey(sqwMap, StringUtils.lowerCase(cellName))) {
                            //识别发布形式
                            if(StringUtils.isNotBlank(cellValue)) {
                                if(StringUtils.length(cellValue) > 200) {
                                    errorList.add(String.format("【%s】长度不能超过200个字符", cellName));
                                } else {
                                    StarQuoteWay starQuoteWay = sqwMap.get(StringUtils.lowerCase(cellName));
                                    if(ObjectUtils.defaultIfNull(starQuoteWay.getStatus(), 1).intValue() == 0) {
                                        errorList.add(String.format("自定义信息【%s】已被禁用", cellName));
                                    } else {
                                        qwdJSONObject.put(starQuoteWay.getWayCode(), cellValue);
                                    }
                                }
                            }
                        } else if(CommonUtil.containsKey(publishFormMap, StringUtils.lowerCase(cellName))) {
                            if(StringUtils.isNotBlank(cellValue)) {
                                //识别自定义信息
                                if(!NumberUtil.isNumber(cellValue)) {
                                    errorList.add(String.format("【%s】请填写数字或置空", cellName));
                                } else {
                                    BigDecimal price = new BigDecimal(cellValue);
                                    if(price.signum() < 0 || price.precision() > 9 || price.scale() > 2) {
                                        errorList.add(String.format("【%s】数值必须大于等于0，长度最多9位且小数位最多2位", cellName));
                                    } else {
                                        BaseSecondlevelDictsVO secondlevelDictsVO = publishFormMap.get(StringUtils.lowerCase(cellName));
                                        if(ObjectUtils.defaultIfNull(secondlevelDictsVO.getDictsStatus(), 0).intValue() == 1) {
                                            errorList.add(String.format("报价信息【%s】已被禁用", cellName));
                                        } else {
                                            qidJSONObject.put(secondlevelDictsVO.getSecondlevelCode(), cellValue);
                                        }
                                    }
                                }
                            }
                        } else {
                            errorList.add(String.format("【%s】未找到该字段", cellName));
                        }
                    }
                });
                starRateCardDetail.setQuoteWayData(qwdJSONObject.toJSONString());
                starRateCardDetail.setQuoteInfoData(qidJSONObject.toJSONString());
                if(CollectionUtils.isNotEmpty(errorList)) {
                    rowErrorMap.put(i, errorList);
                } else {
                    detailList.add(starRateCardDetail);
                }
            }
            if(MapUtils.isNotEmpty(rowErrorMap)) {
                failReasonMap.put(platformName, rowErrorMap);
            }
        }
        if(CollectionUtils.isNotEmpty(detailList)
           && MapUtils.isEmpty(failReasonMap)
        ) {
            starRateCardDetailMapper.delete(Wrappers.lambdaQuery(StarRateCardDetail.class).eq(StarRateCardDetail::getCardId, starRateCard.getId()));
            starRateCardDetailMapper.insertBatch(detailList);
        }
        if(MapUtils.isNotEmpty(failReasonMap)) {
            //生成失败文件
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            ExcelWriter excelWriter = getExcelWriter(outputStream);
            for(Map.Entry<String, Map<Integer, String>> entry : sheetHeaderMap.entrySet()) {
                Map<Integer, List<String>> rowErrorMap = failReasonMap.get(entry.getKey());
                if(MapUtils.isNotEmpty(entry.getValue()) && MapUtils.isNotEmpty(rowErrorMap))  {
                    entry.getValue().put(-1, "失败原因");
                }
                List<Map<Integer, String>> bodyList = sheetBodyMap.get(entry.getKey());
                if(CollectionUtils.isNotEmpty(bodyList) && MapUtils.isNotEmpty(rowErrorMap)) {
                    Integer maxCellNum = entry.getValue().entrySet().stream().map(Map.Entry :: getKey).max(Integer::compareTo).orElse(null);
                    for (int i = 0; i < bodyList.size(); i++) {
                        Map<Integer, String> rowData = bodyList.get(i);
                        if(rowErrorMap.containsKey(i)) {
                            rowData.put(-1, CommonUtil.defaultList(rowErrorMap.get(i)).stream().collect(Collectors.joining(SeparatorCst.LINE_BREAK)));
                        } else {
                            rowData.put(-1, "");
                        }
                        //补全
                        for(int j = 0; j < maxCellNum; j++) {
                            if(!rowData.containsKey(j)) {
                                rowData.put(j, "");
                            }
                        }
                    }
                }
                List<List<String>> newHeadList = entry.getValue().entrySet().stream().sorted(Map.Entry.comparingByKey()).map(e -> Arrays.asList(StringUtils.defaultString(e.getValue()))).collect(Collectors.toList());
                List<List<String>> newBodyList = CommonUtil.defaultList(bodyList).stream()
                        .map(e -> e.entrySet().stream().sorted(Map.Entry.comparingByKey()).map(o -> StringUtils.defaultString(o.getValue())).collect(Collectors.toList())).collect(Collectors.toList());
                excelWriter.write(newBodyList, EasyExcel
                        .writerSheet(entry.getKey())
                        .needHead(true)
                        .head(newHeadList)
                        .build());
            }
            excelWriter.finish();
            return FilestoreHelper.uploadExcel("红人刊例导入失败原因.xlsx", outputStream).getId();
        }
        return null;
    }

    /**
     * 获取ExcelWriter
     * @param outputStream
     * @return
     */
    private ExcelWriter getExcelWriter(ByteArrayOutputStream outputStream) {
        ExcelWriter excelWriter = EasyExcel.write(outputStream)
                .excelType(ExcelTypeEnum.XLSX)
                .registerWriteHandler(getCellStyleStrategy())
                .registerWriteHandler(new AbstractColumnWidthStyleStrategy() {
                    private static final int MAX_COLUMN_WIDTH = 255;
                    @Override
                    protected void setColumnWidth(WriteSheetHolder writeSheetHolder, List<WriteCellData<?>> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
                        if (isHead) {
                            int columnWidth = cell.getStringCellValue().getBytes().length;
                            if (columnWidth > MAX_COLUMN_WIDTH) {
                                columnWidth = MAX_COLUMN_WIDTH;
                            } else {
                                columnWidth = columnWidth + 3;
                            }
                            writeSheetHolder.getSheet().setColumnWidth(cell.getColumnIndex(), columnWidth * 256);
                        }
                        writeSheetHolder.getSheet().createFreezePane(1,0,1,0);
                    }
                }).build();
        return excelWriter;
    }

    /**
     * 设置样式
     * @return
     */
    private HorizontalCellStyleStrategy getCellStyleStrategy() {
        //头的策略
        WriteCellStyle headWriteCellStyle = new WriteCellStyle();
        //设置字体
        WriteFont writeFont = new WriteFont();
        writeFont.setFontName("微软雅黑");
        writeFont.setBold(true);
        writeFont.setFontHeightInPoints((short) 22);
        headWriteCellStyle.setWriteFont(writeFont);
        //设置文本收缩至合适
        headWriteCellStyle.setShrinkToFit(true);
        //自动换行
        headWriteCellStyle.setWrapped(false);
        // 水平对齐方式
        headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        headWriteCellStyle.setFillPatternType(FillPatternType.NO_FILL);
        // 垂直对齐方式
        headWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);

        //内容策略
        WriteCellStyle contentCellStyle = new WriteCellStyle();
        headWriteCellStyle.setShrinkToFit(true);
        contentCellStyle.setWrapped(false);
        contentCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        contentCellStyle.setFillPatternType(FillPatternType.NO_FILL);
        contentCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);

        return new HorizontalCellStyleStrategy(headWriteCellStyle, contentCellStyle);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void approvalCallback(ApprovalFlowDTO approvalFlowDTO, Boolean status) {
        StarRateCard starRateCard = this.getStarRateCard(approvalFlowDTO.getSrcId(), true);
        approvalFlowDTO.setSrcCode(starRateCard.getCardCode());
        approvalFlowDTO.addSubmitInfoIfNull(starRateCard);
        if (Objects.isNull(status)) {
            //审批流配置-接入待办
            return;
        }
        //流程审批结束
        Integer approvalStatus = status.booleanValue() ? ApprovalStatusEnum.PASSED.getCode() : ApprovalStatusEnum.REJECTED.getCode();
        if (ApprovalStatusEnum.anyMatch(starRateCard.getStatus(), ApprovalStatusEnum.PASSED, ApprovalStatusEnum.REJECTED)) {
            log.error("刊例管理-流程审批结束重复回调，不做处理！approvalId={}", approvalFlowDTO.getApprovalId());
            return;
        }
        BusinessAssert.isTrue(DistributedLockTemplate.execute(OperateLockEnum.STAR_RATE_CARD.generateKey(starRateCard.getId()), () -> {
            StarRateCard updateEntity = new StarRateCard();
            updateEntity.setId(starRateCard.getId());
            updateEntity.setStatus(approvalStatus);
            updateEntity.setUpdateBy(approvalFlowDTO.getUserId());
            updateEntity.setUpdateName(approvalFlowDTO.getUserName());
            updateEntity.setUpdateDate(new Date());
            return starRateCardMapper.updateById(updateEntity) > 0;
        }), McnErrorCode.FLOW_CALLBACK_HANDLE_EXCEPTION);

        //发送流程结束通知
        approvalFlowService.sendNotify(approvalFlowDTO, starRateCard.getId(), starRateCard.getCardCode(),
                ApprovalEnum.STAR_RATE_CARD, status, starRateCard.getCreateBy(), starRateCard.getCreateName());
    }

    /**
     *  获取平台与配置ID集合
     * @param configIdList
     * @return
     */
    private Map<String, Long> getPlatforConfigIdMap(List<Long> configIdList) {
        List<StarRateCardConfig> starRateCardConfigList = starRateCardConfigMapper.selectList(Wrappers.lambdaQuery(StarRateCardConfig.class)
                .select(StarRateCardConfig::getId, StarRateCardConfig::getPlatformId)
                .in(StarRateCardConfig::getId, configIdList)
                .eq(StarRateCardConfig::getIsDelete, NumberCst.ZERO));
        return CommonUtil.defaultList(starRateCardConfigList).stream().collect(Collectors.toMap(k -> k.getPlatformId(), v -> v.getId()));
    }

    /**
     * 如果确认则校验启用的报价方式必填-长度等
     * @param quoteWayData
     * @param starQuoteWayList
     */
    private void verifyQuateWayIfConfirmed(String quoteWayData, List<StarQuoteWay> starQuoteWayList) {
        JSONObject jsonObject = CommonUtil.defaultParseObject(quoteWayData);
        if(CollectionUtils.isEmpty(starQuoteWayList)) {
            return;
        }
        starQuoteWayList.forEach(starQuoteWay -> {
            if(starQuoteWay.getRequired().booleanValue()) {
                String wayValue = jsonObject.getString(starQuoteWay.getWayCode());
                String errorPrefix = "【" + starQuoteWay.getWayName()+"】";
                BusinessAssert.notBlank(wayValue, McnErrorCode.PARAM_ERROR, errorPrefix+"不能为空");
                QuoteDataTypeEnum quoteDataTypeEnum = QuoteDataTypeEnum.toEnum(starQuoteWay.getDataType());
                switch (quoteDataTypeEnum) {
                    case TEXT:
                        BusinessAssert.maxLength(wayValue, quoteDataTypeEnum.getMaxLength(), McnErrorCode.PARAM_ERROR, errorPrefix+"最大长度不能超出%s个字符");
                        break;
                    case NUM:
                        try {
                            BigDecimal b = new BigDecimal(wayValue);
                            BusinessAssert.isTrue(b.compareTo(BigDecimal.ZERO) >= 0, McnErrorCode.PARAM_ERROR, errorPrefix+"数字须大于0");
                            BusinessAssert.isTrue(b.scale() <= 2 && b.precision() <= quoteDataTypeEnum.getMaxLength(), McnErrorCode.PARAM_ERROR, errorPrefix+"数字须长度<=10且小数位<=2");
                            //订正数字格式 比如录入：0000100等
                            jsonObject.put(starQuoteWay.getWayCode(), b.toString());
                        } catch (Exception e) {
                            throw new BusinessException(McnErrorCode.PARAM_ERROR.code(), errorPrefix+"数字格式错误");
                        }
                        break;
                    case DICT:
                        break;
                    default :
                        break;
                }
            }
        });
    }

    /**
     * 如果确认则校验启用的报价信息必填-长度等
     * @param quoteInfoData
     * @param platformId
     */
    private void verifyQuateInfoIfConfirmed(String quoteInfoData, String platformId) {
        Map<String, String> publishFormDictMap = baseInfoClient.getEnabledSecondlevelDictMap(DictTypeCst.PUBLISHING_PLATFORM).get(platformId);
        if(MapUtils.isEmpty(publishFormDictMap)) {
            return;
        }
        Map<String, BigDecimal> quateInfoDataMap = JSONObject.parseObject(quoteInfoData, new TypeReference<Map<String, BigDecimal>>() {});
        BusinessAssert.notEmpty(quateInfoDataMap, McnErrorCode.BUSINESS_ERROR, "报价信息不能为空");
        for(Map.Entry<String, BigDecimal> entry : quateInfoDataMap.entrySet()) {
            if(publishFormDictMap.containsKey(entry.getKey())) {
                BigDecimal amount = entry.getValue();
                boolean amountOk = Objects.nonNull(amount) && amount.compareTo(BigDecimal.ZERO) >= 0 && amount.scale() <= 2;
                if(!amountOk) {
                    String publishFormName = CommonUtil.getMapValue(publishFormDictMap, entry.getKey());
                    throw new BusinessException(McnErrorCode.BUSINESS_ERROR.code(), String.format("【%s】报价-请输入数字且不超过2位小数！", StringUtils.defaultString(publishFormName, entry.getKey())));
                }
            }
        }
    }


    /**
     * 多选字段数据字典带逗号返回名称
     *
     * @param dictProfileList 请求参数
     * @param dictList        请求参数
     * @return 返回值
     */
    private  Map<String, String> searchSomething(List<String> dictProfileList, List<String> dictList) {
        Map<String, String> dictMap = null;
        if (Objects.isNull(dictList)) {
            return null;
        }
        List<String> strings = new ArrayList<>();
        for (String s : dictList) {
            if (StringUtils.isNotBlank(s)) {
                if (s.contains(SeparatorCst.COMMA)) {
                    String[] split = s.split(SeparatorCst.COMMA);
                    if (Objects.nonNull(split)) {
                        strings.addAll(Arrays.asList(split));
                    }
                } else {
                    strings.add(s);
                }
            }
        }
        try {
            dictMap = Objects.requireNonNull(baseInfoClient.getDescByCode(new DescByCodeDTO().setDictsCodes(strings).setProfileCodes(dictProfileList))).getObj();

        } catch (Exception e) {
            log.error("feign 获取字典错误", e);
        }
        return dictMap;
    }

    /**
     *
     * @param key
     * @param dictMapping
     * @param dictProfile
     * @return
     */
    private String getDictValues(String key, Map<String, String> dictMapping, String dictProfile) {
        List<String> list = new ArrayList<>();
        String value;
        if (org.apache.commons.lang.StringUtils.isNotBlank(key) && key.contains(SeparatorCst.COMMA)) {
            String[] strings = key.split(SeparatorCst.COMMA);
            for (String code : strings) {
                value = dictMapping.get(dictProfile + SeparatorCst.SEMICOLON + code);
                list.add(value);
            }
        } else {
            list.add(dictMapping.get(dictProfile + SeparatorCst.SEMICOLON + key));
        }
        StringBuilder dictValue = new StringBuilder();
        if (list.size() > 0) {
            for (int i = 0; i < list.size(); i++) {
                if (Objects.nonNull(list.get(i))) {
                    if (i == 0) {
                        dictValue.append(list.get(i));
                    } else {
                        dictValue.append(SeparatorCst.COMMA).append(list.get(i));
                    }
                }
            }
        }
        if (dictValue == null) {
            return null;
        }
        return dictValue.toString();
    }

    /**
     * 按刊例配置获取报价方式Map
     * @param configIds
     * @return
     */
    private Map<Long, List<StarQuoteWay>> getStarQuoteWayMap(List<Long> configIds) {
        return CommonUtil.defaultList(starQuoteWayMapper.selectList(Wrappers.lambdaQuery(StarQuoteWay.class)
                .in(StarQuoteWay::getConfigId, configIds)
                .eq(StarQuoteWay::getStatus, NumberCst.ONE)
                .eq(StarQuoteWay::getIsDelete, NumberCst.ZERO))).stream().collect(Collectors.groupingBy(StarQuoteWay::getConfigId));
    }

}
